package logger

type Logger struct {
	Config LoggerConfig
	Level  Level

	formatter IFormatter
	target    []Target

	prefix [][2]string

	fSwitchCheck  func() bool
	switchChecker *SwitchChecker
}

func NewLogger(cfg *LoggerConfig) (*Logger, error) {
	var _cfg LoggerConfig
	if cfg == nil {
		_cfg = LoggerConfig{} //TODO:: default config
	} else {
		_cfg = *cfg
	}
	_cfg.FixDefault()

	l := &Logger{
		Config: _cfg,

		Level: DefaultGlobalLevel,

		formatter: _cfg.Formatter,
		target:    nil,

		prefix: make([][2]string, 0, 4),
	}

	if !l.Config.IgnoreStdout {
		l.target = append(l.target, DefaultTargetStdout)
	}

	if l.Config.SwitchChecker != nil {
		l.switchChecker = l.Config.SwitchChecker
		l.fSwitchCheck = l.doSwitchCheck
	} else {
		l.fSwitchCheck = l.noSwitchCheck
	}

	return l, nil
}

func (l *Logger) Clone() *Logger {
	nl := &Logger{
		Config:    l.Config,
		Level:     l.Level,
		formatter: l.formatter,
		target:    make([]Target, len(l.target)),
		prefix:    make([][2]string, len(l.prefix)),
	}
	copy(nl.target, l.target)
	copy(nl.prefix, l.prefix)
	if nl.switchChecker != nil {
		nl.switchChecker = nl.switchChecker.Clone()
		nl.fSwitchCheck = nl.doSwitchCheck
	} else {
		nl.fSwitchCheck = nl.noSwitchCheck
	}
	return nl
}

// unsafe for concurrent use
// should be called before dispatch and any log
func (l *Logger) AddTarget(t Target) {
	l.target = append(l.target, t)
}

func (l *Logger) AppendPrefix(prefix string) {
	l.prefix = append(l.prefix, [2]string{prefix, ""})
}

func (l *Logger) AppendPrefixExclusive(prefix string, key string) {
	for i, pair := range l.prefix {
		if pair[1] == key {
			l.prefix[i][0] = prefix
			return
		}
	}
	l.prefix = append(l.prefix, [2]string{prefix, key})
}

func (l *Logger) getCtx(lv Level) *Context {
	if lv < l.Level {
		return nil
	}
	if !l.fSwitchCheck() {
		return nil
	}

	//get context from pool
	ctx := l.Config.ContextPool.Get()
	ctx.prepare(l, lv)

	return ctx
}

func (l *Logger) TRC() IContext {
	return l.getCtx(LevelTrace)
}

func (l *Logger) DBG() IContext {
	return l.getCtx(LevelDebug)
}

func (l *Logger) INF() IContext {
	return l.getCtx(LevelInfo)
}

func (l *Logger) WRN() IContext {
	return l.getCtx(LevelWarn)
}

func (l *Logger) ERR() IContext {
	return l.getCtx(LevelError)
}

func (l *Logger) FAT() IContext {
	return l.getCtx(LevelFatal)
}

func (l *Logger) SwitchChecker() *SwitchChecker {
	return l.switchChecker
}

func (l *Logger) doSwitchCheck() bool {
	return l.switchChecker.Check(GlobalSwitch)
}

func (l *Logger) noSwitchCheck() bool {
	return true
}
